'''
 * CaRComp Execution Environment
 * 
 * Copyright (C) 2011 Taylor L. Riche
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
'''

from carcomp.framework import component, abstraction, connector
from carcomp.directory import localhost
from carcomp.apps.webserver import constants

class ServerAbs(abstraction.Abstraction):
    
    def __init__(self, name):
        abstraction.Abstraction.__init__(self, name)
        sc = ServerComp('Server_abs0({})'.format(name))
        self.add_component(sc)
        self.add_port('incoming', (sc, 'incoming'))
        self.add_port('outgoing', (sc, 'outgoing'), output=True)
        
class MRServerAbs(abstraction.Abstraction):
    
    def __init__(self, name):
        abstraction.Abstraction.__init__(self, name)
        mc = MapComp('Map_abs0')
        rc = ReduceComp('Reduce_abs1')
        self.add_component(mc)
        self.add_component(rc)
        for i in range(constants.REP_NUM):
            sc = ServerAbs('Server_alg{}'.format(1+i))
            self.add_component(sc)
        self.add_port('incoming', (mc, 'in'))
        self.add_port('outgoing', (rc, 'out'), output=True)
        for i in range(constants.REP_NUM):
            conn = connector.Connector(mc.get_endpoint('out{}'.format(i)), 
                                       self.get_component('Server_alg{}'.format(i+1)).get_endpoint('incoming'))
            self.add_connector(conn)
            conn = connector.Connector(self.get_component('Server_alg{}'.format(i+1)).get_endpoint('outgoing'),
                                       rc.get_endpoint('in{}'.format(i)))
            self.add_connector(conn)
            
class MapComp(component.Implementation):
    
    def __init__(self, name):
        component.Implementation.__init__(self, name)
        self.add_port(component.Port('in', 'map_request'))
        for i in range(constants.REP_NUM):
            self.add_port(component.Port('out{}'.format(i), '__OUT__'))
            
    def map_request(self, msg):
        req = msg.strip().split(':')
        target = hash(req[1]) % constants.REP_NUM
        self.send_out('out{}'.format(target), msg)

class ReduceComp(component.Implementation):

    def __init__(self, name):
        component.Implementation.__init__(self, name)
        self.add_port(component.Port('out', '__OUT__'))
        for i in range(constants.REP_NUM):
            self.add_port(component.Port('in{}'.format(i), 'reduce_request'))
            
    def reduce_request(self, msg):
        if ServerComp.DEBUG:
            print(msg)
        self.send_out('out', msg)

class ServerComp(component.Implementation):
    '''
    classdocs
    '''

    FILE_NOT_FOUND_MSG = 'ERROR:FILE NOT FOUND'
    BAD_CMD_MSG = 'ERROR:BAD COMMAND'
    
    READ_CMD = 'READ_F'
    WRITE_CMD = 'WRITE_F'
    
    DEBUG = False

    def __init__(self, name):
        '''
        Constructor
        '''
        component.Implementation.__init__(self, name)
        self.add_port(component.Port('incoming', 'process_request'))
        self.add_port(component.Port('outgoing', '__OUT__'))
        self._file_list = []
        for i in range(10):
            self._file_list.append(localhost.local_directory.add_root('ft/webserver/data/test_file_{}.txt'.format(i)))
        
    def process_request(self, msg):
        '''
        Look up filename and send file data out to "client"
        
        Request format:
            COMMAND:CMD_DATA
            
        Commands recognized:
            READ_F : CMD_DATA is then equal to the filename to be read
            WRITE_F : CMD_DATA is then a filename, a ':', and then the data to be written
        '''
        req = msg.strip().split(':')
        fn = localhost.local_directory.add_root('ft/webserver/data/{}'.format(req[1]))
        if ServerComp.DEBUG:
            print(req[0], req[1], fn)
        if fn not in self._file_list:
            self.send_out('outgoing', ServerComp.FILE_NOT_FOUND_MSG)
        elif req[0] == ServerComp.READ_CMD:
            read_f = open(fn, 'r')
            out_msg= ''
            for line in read_f:
                out_msg += line
            self.send_out('outgoing', out_msg)
            read_f.close()
        elif req[0] == ServerComp.WRITE_CMD:
            write_f = open (fn, 'w')
            write_f.write(req[2])
            write_f.close()
        else:
            self.send_out('outgoing', ServerComp.BAD_CMD_MSG) 
            
        